﻿using System;
using System.Drawing;
using System.Linq;
using CanvasControl.Model;
using CanvasControl.View;
using GraphDrawing.Graph.Model;
using GraphDrawing.Graph.View;

namespace GraphDrawing.Graph.Behaviors
{
    /// <summary>
    /// Состояние выделения нескольких блоков с помощью рамки.
    /// </summary>
    public class SelectionBoxStatePresenter : ModelStatePresenter, IPainter
    {
        readonly SelectionBoxModel model;
        readonly ViewPortModel viewPort;

        public SelectionBoxStatePresenter(ModelStateContext context, Point originPoint) : base(context)
        {
            viewPort = context.CanvasModel.ViewPort;
            model = new SelectionBoxModel(context, originPoint);
            context.CanvasModel.AddPainter(PaintStages.POSTPROCESSING, this);
        }

        public override void OnMouseMove(Point p)
        {
            model.UpdateBoxPosition(p);
            context.Canvas.UpdateView();
        }

        public override void OnMouseUp(Point p)
        {
            context.CanvasModel.RemovePainter(this);
        }

        public void Draw(DrawingContext dc)
        {
            var view = context.ViewContainer.GetView<ISelectionBoxView>();
            var box = viewPort.GlobalToClient(model.SelectionBox);
            view.Draw(dc, box);
        }

        /// <summary>
        /// Модель рамки выделения.
        /// </summary>
        class SelectionBoxModel
        {
            readonly ViewPortModel viewPort;
            readonly GraphModel graph;
            readonly Point originPoint;

            /// <summary>
            /// Прямоугольник, описывающий границы области выделения.
            /// </summary>
            public Rectangle SelectionBox { get; private set; }

            public SelectionBoxModel(ModelStateContext context, Point originPoint)
            {
                graph = context.Graph;
                viewPort = context.CanvasModel.ViewPort;
                this.originPoint = originPoint;
            }

            /// <summary>
            /// Выполняет расчёт рамки выделения.
            /// </summary>
            /// <param name="p">Координаты текущей точки в координатах области отображения.</param>
            public void UpdateBoxPosition(Point p)
            {
                var box = CalculateSelectionBox(viewPort.ClientToGlobal(p));
                graph.ClearSelection();
                foreach (var block in graph.EnumerateBlocks(true).Where(b => box.IntersectsWith(b.GetBounds())))
                    graph.Select(block);
                SelectionBox = box;
            }

            /// <summary>
            /// Рассчитывает границы области выделения между точкой начала выделения и текущей точкой.
            /// </summary>
            /// <param name="viewPoint">Текущая точка.</param>
            Rectangle CalculateSelectionBox(Point viewPoint)
            {
                var left = Math.Min(originPoint.X, viewPoint.X);
                var top = Math.Min(originPoint.Y, viewPoint.Y);
                var width = Math.Abs(originPoint.X - viewPoint.X);
                var height = Math.Abs(originPoint.Y - viewPoint.Y);
                return new Rectangle(left, top, width, height);
            }
        }
    }
}